﻿using GN.Pay.Application.Dto.Input;
using GN.Pay.Application.Dto.Output;
using GN.Pay.Core.Exceptions;
using GN.Pay.Core.Services;
using GN.Pay.Utils;
using GN.Pay.Utils.Security;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.Application.GatewayApp.Handle
{
    /// <summary>
    /// 网关管理
    /// </summary>
    public class GatewayManager
    {
        private const long Max_TimeStamp_Diff = 1000 * 60 * 5;

        private static void ParamCheck(string value, string name)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                PayExceptionUtils.ThrowPayValidationException(name + " 为 null 或空白值");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        private static void CheckInput(ServiceSignInput input)
        {
            if (input == null)
            {
                PayExceptionUtils.ThrowPayValidationException("输入 input 为 null");
            }
            ParamCheck(input.AppId, nameof(input.AppId));
            ParamCheck(input.MerchantId, nameof(input.MerchantId));
            ParamCheck(input.RandomStr, nameof(input.RandomStr));
            ParamCheck(input.Service, nameof(input.Service));
            ParamCheck(input.Sign, nameof(input.Sign));
            if (Math.Abs(DateTimeUtils.CurrentUtcTimeStampMilliseconds - input.TimeStamp) > Max_TimeStamp_Diff)
            {
                PayExceptionUtils.ThrowPayValidationException("TimeStamp 值与服务器值超过限定范围。");
            }
        }

        /// <summary>
        /// 请求验证检查
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool RequestSignCheck(ServiceSignInput input)
        {
            CheckInput(input);
            var tradeCacheService = IocManagerUtils.IocManager.Resolve<ITradeCacheService>();
            var app = tradeCacheService.GetMerchantAppInfo(input.AppId);
            if (app == null)
            {
                PayExceptionUtils.ThrowPayValidationException("无权访问");
            }
            SignDictionary signDic = new SignDictionary();
            signDic["service"] = input.Service;
            signDic["merchantId"] = app.MerchantId;
            signDic["appId"] = input.AppId;
            signDic["randomStr"] = input.RandomStr;
            signDic["timeStamp"] = input.TimeStamp.ToString();
            signDic["params"] = input.Params;
            string content = signDic.UrlLinkString() + "&key=" + app.AppKey;
            return Md5Utils.SginVerify(content, input.Sign, Encoding.UTF8);
        }

        /// <summary>
        /// 成功响应输出
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="result"></param>
        /// <param name="isCamelCase"></param>
        /// <returns></returns>
        public static ServiceMessageOutput SuccessResponseOutput(string appId,
            object result, bool isCamelCase)
        {
            var tradeCacheService = IocManagerUtils.IocManager.Resolve<ITradeCacheService>();
            var app = tradeCacheService.GetMerchantAppInfo(appId);
            if (app == null)
            {
                PayExceptionUtils.ThrowPayValidationException("无权访问");
            }
            var output = new ServiceSignMessageOutput();
            output.AppId = appId;
            output.MerchantId = app.MerchantId;
            output.RandomStr = Guid.NewGuid().ToString("N");
            output.Success = true;
            output.TimeStamp = DateTimeUtils.CurrentUtcTimeStampMilliseconds;
            output.Result = result;
            string content = output.ToJson(isCamelCase) + "&key=" + app.AppKey;
            string sign = Md5Utils.Sign(content, Encoding.UTF8);
            return new ServiceMessageOutput(output, sign);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="requestDictionary"></param>
        /// <returns></returns>
        public static ServiceSignInput RequestServiceInput(IDictionary<string, string> requestDictionary)
        {
            ServiceSignInput input = new ServiceSignInput();
            input.Service = GetRequestResultParam(requestDictionary, "service");
            input.AppId = GetRequestResultParam(requestDictionary, "appId");
            input.MerchantId = GetRequestResultParam(requestDictionary, "merchantId");
            input.RandomStr = GetRequestResultParam(requestDictionary, "randomStr");
            input.TimeStamp = GetRequestResultParam(requestDictionary, "timeStamp", Convert.ToInt64);
            input.Sign = GetRequestResultParam(requestDictionary, "sign");
            string inputParams;
            if (requestDictionary.TryGetValue("params", out inputParams))
            {
                input.Params = inputParams;
            }
            if (!RequestSignCheck(input))
            {
                PayExceptionUtils.ThrowPayValidationException("签名不正确");
            }
            return input;
        }

        /// <summary>
        /// 获取请求参数
        /// </summary>
        /// <param name="requestDictionary"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private static string GetRequestResultParam(IDictionary<string, string> requestDictionary,
         string name)
        {
            return GetRequestResultParam<string>(requestDictionary, name, Convert.ToString);
        }

        /// <summary>
        /// 获取请求参数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="requestDictionary"></param>
        /// <param name="name"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        private static T GetRequestResultParam<T>(IDictionary<string, string> requestDictionary,
            string name, Func<string, T> convert)
        {
            string value;
            if (requestDictionary.TryGetValue(name, out value) && !string.IsNullOrWhiteSpace(value))
            {
                try
                {
                    return convert(value.Trim());
                }
                catch (Exception)
                {
                    throw PayExceptionUtils.ThrowPayDataFormatException("参数 " + name + " 格式不正确");
                }
            }
            throw PayExceptionUtils.ThrowPayValidationException("缺少参数 " + name);
        }
    }
}
